﻿/************************************************************************
 * SMLib - State Model Library
 * Copyright (C) 2012-2013 Ali Khalili (khalili_DOT_ir_@SIGN_gmail_DOT_com)
 * **********************************************************************
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License (LGPL) published 
 * by the Free Software Foundation; either version 2.1 of the License, or (at your
 * option) any later version. This program is distributed without any
 * warranty. See the GNU Lesser General Public License for more details. 
 * ***********************************************************************
 * File         :     SMLib\Utils\Hashing\HashAlgos_ValOrdering.cs
 * Description  :     This is a collection of hash algorithms (originaly from NModel)
 * Change log   :     -Jan 2012:  First version
 * To Do        :     
 ************************************************************************/
using System;
using System.Collections.Generic;
using System.Text;


namespace SMLib.Utils.Hashing
{
    /// <summary>
    /// A collection of Hash Algorithms
    /// </summary>
    public static class HashAlgos_ValOrdering
    {
        //static Random globalChoiceController = new Random(typeof(HashAlgorithms).GetHashCode());

        ///// <summary>
        ///// The global choice controller used by the <see cref="ICollectionValue&lt;T&gt;.Choose()" /> method.
        ///// </summary>
        ///// <seealso cref="ICollectionValue&lt;T&gt;.Choose()" />
        //public static Random GlobalChoiceController
        //{
        //    get { return globalChoiceController; }
        //    set { globalChoiceController = value; }
        //}

        #region Value Ordering [Part of NModel]
        ///// <summary>
        ///// Calculates value order (for hashing of unordered types like sets and maps)
        ///// </summary>
        ///// <typeparam name="T"></typeparam>
        ///// <param name="value1"></param>
        ///// <param name="value2"></param>
        ///// <returns></returns>
        //public static int Compare<T>(T/*?*/ value1, T/*?*/ value2) <T>
        ////^ ensures result == 0 ==> Object.Equals(value1, value2);
        ////^ ensures result == -1 || result == 0 || result == 1;
        //{
        //    bool isNull1 = ((object)value1 == null);
        //    bool isNull2 = ((object)value2 == null);
        //    if (isNull1 && !isNull2) return -1;  // null is always the minimal element
        //    if (!isNull1 && isNull2) return 1;
        //    if (isNull1 && isNull2) return 0;
        //    return value1.CompareTo(value2);
        //}

        ///// <summary>
        ///// Calculates a total ordering of terms. There are eight cases:
        ///// <list>
        /////     <item>The items are (pointer) equal</item>
        /////     <item>One of the values is null; null is treated as the minimal element</item>
        /////     <item>Both of the values are of type <see cref="LabeledInstance" />; comparison of labels is used</item>
        /////     <!--<item>Both of the values are of type <see cref="EnumeratedInstance" />; comparison of labels is used</item>-->
        /////     <item>For values of unequal types, we use dictionary order of type names</item>
        /////     <item>For equal types, the <see cref="IComparable" /> interface is used if available.</item>
        /////     <item>For equal types, where <c>IComparable</c>cannot be used, we check for <c>Object.Equals</c></item>
        /////     <item>As a default, in the case where none of the preceding can be used to order values, we 
        ///// intern the values in a global table using <see cref="HashAlgorithms.InternObject" /> and do
        ///// comparision on the integer tokens returned from the intern operation.</item>
        ///// </list>
        ///// </summary>
        ///// <param name="value1">The first value</param>
        ///// <param name="value2">The second value</param>
        ///// <returns>-1 if <paramref name="value1"/> is less than <paramref name="value2"/>, 0 if equal, 1 otherwise.</returns>
        ///// <remarks>
        ///// One of the uses of term ordering is to disambiguate distinct items with the same hash generatedCode
        ///// within the representation of unordered data types like sets.
        ///// </remarks>
        //// Bogus FxCop message-- complained that value1 and value2 were not checked for null; they are.
        //public static int CompareValues(object/*?*/ value1, object/*?*/ value2)
        ////^ ensures result == 0 <==> Object.Equals(value1, value2);
        ////^ ensures result == -1 || result == 0 || result == 1;
        //{
        //    // Case 1: objects are pointer-equal
        //    if ((object)value1 == value2) return 0;

        //    // Case 2: one of the values is null: treat null as the minimal element
        //    bool isNull1 = (value1 == null);
        //    bool isNull2 = (value2 == null);
        //    if (isNull1 && !isNull2) return -1;
        //    if (!isNull1 && isNull2) return 1;

        //    // Case 3: both of the values are instances of classes with user-provided labeling: use labels to compare
        //    LabeledInstance i1 = value1 as LabeledInstance;
        //    if ((object)i1 != null && value2 is LabeledInstance)
        //        return i1.CompareTo(value2);

        //    //// TODO: factor LabeledIntance and EnumeratedInstance to share a common base class
        //    //EnumeratedInstance e1 = value1 as EnumeratedInstance;
        //    //if ((object)e1 != null && value2 is EnumeratedInstance)
        //    //    return e1.CompareTo(value2);

        //    // Case 5: objects are of unequal types: do type compare in dictionary order 
        //    Type t1 = value1.GetType();
        //    Type t2 = value2.GetType();
        //    if (t1 != t2)
        //        return t1.ToString().CompareTo(t2.ToString());

        //    // Case 6: objects are of the same type and implement IComparable; use IComparable to order
        //    IComparable comparable = value1 as IComparable;
        //    if ((object)comparable != null)
        //        return comparable.CompareTo(value2);

        //    // Case 7: objects are types; use string compare of type names
        //    if (value1 is Type)
        //        return value1.ToString().CompareTo(value2.ToString());


        //    // Case 8: objects are of the same type, do not support IComparable but are Equal but not reference equal
        //    // This is not supported. There is no way to order instances of a class that overrides Equals but 
        //    // does not also implement IComparable.
        //    if (Object.Equals(value1, value2))
        //        throw new ArgumentException("Cannot apply value comparision to value of type that overrides Object.Equals but does not implement IComparable.");

        //    // Case 9 (default): use a table to order objects. This is introduces a memory leak if the user
        //    // does not explicitly call UninternObject, but it acts as an ordering of last resort
        //    // for types that do not override Equals and GetHashCode. It can be a source of hidden internal nondeterminism.
        //    // It is recommended that this feature not be used.
        //    int int1 = InternObject(value1);
        //    int int2 = InternObject(value2);
        //    return int1.CompareTo(int2);
        //}

        // TO DO: maybe use per-type counters
        static int objectCounter = 0;
        // static int NextObjectId(object o) { return objectCounter++; }

        /// <summary>
        /// Establishes a total ordering on objects by interning them in a global table. 
        /// Used for hashing and term ordering. 
        /// </summary>
        /// <param name="o">The object to intern</param>
        /// <returns>An integer representing the value id.</returns>
        /// <seealso cref="HashAlgos_ValOrdering.CompareValues"/>
        public static int InternObject(object o)
        {
            int result;
            if (objectIdCache.TryGetValue(o, out result))
                return result;
            else
            {
                result = objectCounter++;
                objectIdCache[o] = result;
                return result;
            }
        }

        /// <summary>
        /// Frees memory used to order objects interned via the 
        /// <seeref cref="InternObject"/> method. May only be called when
        /// <paramref name="o"/> is no longer referenced by any data structure
        /// such a set or a map that might have previously used the interning
        /// for term ordering.
        /// </summary>
        /// <param name="o"></param>
        public static void UninternObject(object o)
        {
            objectIdCache.Remove(o);
        }

        static Dictionary<object, int> objectIdCache = new Dictionary<object, int>();

        #endregion





    }

}
